#ifndef _PPLZ_H_
#define _PPLZ_H_

#include <string.h>
#include <stdlib.h>

#include "typedef.h"

/* use a sliding window with size M4_MAX_OFFSET(49k) */

/*
   Format of the marker bytes

   76543210
   --------
   00000001   rrrrrrrr    R1 run, (r - 17)(r >= 18), 0 for 255
   0000rrrr   -           R2 run, (r - 2)(4 <= r <= 17)
   -          -           R3 run, use match flag r(1 <= r <= 3), see below

   lllooorr   oooooooo    M2 match(2 <= (l - 1) <= 7, o - 1, max o = 0x800

   001lllll   oooooorr    oooooooo                  M3 match(1 <= (l - 2) <= 31, o - 1, max o = 0x4000
   00100000   llllllll    oooooorr    oooooooo      M3 match(l - 33 > 0, 0 for 255)

   0001olll   oooooorr    oooooooo                  M4 match(1 <= (l - 2) <= 6, o - 0x4000, max o = 0xbfff
   0001o000   llllllll    oooooorr    oooooooo      M4 match(l - 9 > 0, 0 for 255)

   00000000   ooooooo0 o(3B) llllllrr llllllll      M1 match(1 <= l <= 16383, 1 <= o <= 2G - 1)
   00000000   ooooooo1 o(3B) (2B) llllllrr llllllll M1 match(16384 <= l <= 1G - 1), 1 <= o <= 2G - 1)
*/

/* DO NOT MODIFY definition below */
#define MAX_BUFFER_SIZE (2048u * 1024 * 1024)

#define M2_MAX_OFFSET   0x0800
#define M3_MAX_OFFSET   0x4000
#define M4_MAX_OFFSET   0xbfff

#define M2_MIN_LEN      3
#define M2_MAX_LEN      8
#define M3_MIN_LEN      3
#define M3_MAX_LEN      33
#define M4_MIN_LEN      3
#define M4_MAX_LEN      9

#define M2_MARKER       64
#define M3_MARKER       32
#define M4_MARKER       16

#define M1_MARKER       0
#define M1_SHORT_LEN    16383
#define M1_MAX_OFFSET   (1024u * 1024 * 1024 - 1)

#define R1_MARKER       1
#define R1_ADD          15

#define R2_FIRST_LEN    15

#define R2_MAX_LEN      17
#define R2_MIN_LEN      4
#define R2_ADD          2

#define R3_MAX_LEN      3

/****************************************
 * common definition 
 ***************************************/
#define INLINE  __inline__

#define MAX(a, b)   (((a) > (b))? (a):(b))
#define MIN(a, b)   (((a) < (b))? (a):(b))

#define MEMCPY(dest,src,len)    \
do {    \
    memcpy(dest,src,len); dest += len; src += len;   \
}while(0)

#define MEMCPY_SHORT(dest,src,len) \
    do *dest++ = *src++; while (--len > 0)

//#define ASSERT(x)	
#define ASSERT(x)	assert(x)

/***********************************************************************
 * Overrun detection is internally handled by these macros:
 *
 *  NEED_IP    test input overrun at every input byte
 *  NEED_OP    test output overrun at every output byte
 *  TEST_LB    test match postion
************************************************************************/
/* decode */
#define NEED_IP(x)      if ((uint)(ip_end - ip) < (uint)(x))  goto input_overrun
#define NEED_OP(x)      if ((uint)(op_end - op) < (uint)(x))  goto output_overrun
#define TEST_LB(m_pos)  if (m_pos < out || m_pos >= op) goto lookbehind_overrun

/*********************************************
 *  hash function 
 ********************************************/

/* for hash_index[] */
#include "pplz_rand.h"

typedef struct 
{
    const uchar **l_dict;
    const uchar **s_dict;
}pplz_hash_t;

#define USE_64BIT   1

/* long range hash */

/* recommend long range hash size 
 * window size      hash size
 *      <= 32M      1M
 *         64M      2M
 *         ...      ...
 *          1G      32M 
 *          2G      64M
 */   

#define M1_MIN_LEN          32
#define L_HASH_SIZE         0x80000 /* 512k */
#define L_HASH_MARK         (L_HASH_SIZE - 1)
#define L_HASH_RECORD_MARK  ((M1_MIN_LEN - 1) >> 3)

/* 32(M1_MIN_LEN) bytes comprare */
#if USE_64BIT == 1
#   define M1_CMP(a, b) ((((long *)(a))[0] == ((long *)(b))[0]) \
        && (((long *)(a))[1] == ((long *)(b))[1]) \
        && (((long *)(a))[2] == ((long *)(b))[2]) \
        && (((long *)(a))[3] == ((long *)(b))[3]))
#else
#   define M1_CMP(a, b) ((((int *)(a))[0] == ((int *)(b))[0]) \
        && (((int *)(a))[1] == ((int *)(b))[1]) \
        && (((int *)(a))[2] == ((int *)(b))[2]) \
        && (((int *)(a))[3] == ((int *)(b))[3]) \
        && (((int *)(a))[4] == ((int *)(b))[4]) \
        && (((int *)(a))[5] == ((int *)(b))[5]) \
        && (((int *)(a))[6] == ((int *)(b))[6]) \
        && (((int *)(a))[7] == ((int *)(b))[7]))
#endif

#define L_HASH_INIT(dict)   dict = calloc(L_HASH_SIZE, sizeof(uchar *))
#define L_HASH_FREE(dict)   free(dict)

#define L_HASH_FIRST(h, p)  do {            \
    const uchar *q = p;                     \
    for ((h) = 0; q < p + M1_MIN_LEN; ++q)  \
        (h) ^= hash_index[*q];              \
}while(0)    

#define L_HASH_NEXT(h, p)   do {            \
    (h) ^= hash_index[p[-1]];               \
    (h) ^= hash_index[p[M1_MIN_LEN - 1]];   \
}while(0)

#define L_INDEX(x)          ((x) & L_HASH_MARK)

/* short range hash */
#define S_HASH_SIZE         0x20000 /* 128k */
#define S_HASH_MARK         (S_HASH_SIZE - 1)

#define S_SHIFT              5

#define S_HASH_INIT(dict)   dict = calloc(S_HASH_SIZE, sizeof(uchar *))
#define S_HASH_FREE(dict)   free(dict)

#define S_HASH_FIRST(h, p)   (h) = (((((p)[0] << S_SHIFT) ^ (p)[1]) << S_SHIFT) ^ (p)[2])
#define S_HASH_NEXT(h, p)   do {    \
    (h) ^= (p)[-1] << (S_SHIFT * 2);  \
    (h) = ((h) << 5) ^ (p)[2];      \
}while(0)

#define S_INDEX(x)          ((x) & S_HASH_MARK)

#define BYTES_LEFT          MAX(M1_MIN_LEN, M2_MAX_LEN + 5)

/* error code */
#define PP_E_OK                     0
#define PP_E_INPUT_NOT_CONSUMED     -1
#define PP_E_INPUT_OVERRUN          -2
#define PP_E_OUTPUT_OVERRUN         -3
#define PP_E_EOF_NOT_FOUND          -4
#define PP_E_LOOKBEHIND_OVERRUN     -5
#define PP_E_DICT_NOT_READY         -6
#define PP_E_BUFFER_TOO_LARGE       -7

/* interface */
extern pplz_hash_t *pplz_new();
extern void pplz_free(pplz_hash_t *ph);

extern int pplz_encode(pplz_hash_t *ph, const uchar *in, uint in_len, uchar *out, uint *out_len);

extern int pplz_decode(const uchar *in, uint in_len, uchar *out, uint *out_len);

#endif // _PPLZ_H_
